Skip to content

th-1cc9fa: model-output ceiling clamp + raise starvation defaults (.NET parity)#196

Open
brentrager wants to merge 1 commit into
localflavor-hardeningfrom
th-1cc9fa-dotnet-ceiling
Open

th-1cc9fa: model-output ceiling clamp + raise starvation defaults (.NET parity)#196
brentrager wants to merge 1 commit into
localflavor-hardeningfrom
th-1cc9fa-dotnet-ceiling

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

What

.NET server parity for the model-output-token ceiling clamp + starvation-default raise (siblings: TS #195, Python #194; Rust already merged).

  • ModelInfo — parse/fetch the model's max_output_tokens from the gateway's /model/info (C# analog of the Rust admin::model_output_ceiling + map_model_info). Best-effort: any gateway error / unknown model / missing ceiling ⇒ null ⇒ unclamped.
  • TurnLimits — DI carrier (like ConfirmTools) with the raised defaults (max_tokens 512→8192, iterations 6→20) + the model ceiling. Threaded FrameDispatcher → TurnRunner → AgentOptions (MaxOutputTokens / ModelMaxOutputTokens / MaxIterations); the engine clamps max_tokens to min(budget, ceiling).
  • Program.cs — resolves the ceiling once at boot (single-model server, vs the Rust per-turn lazy cache) and registers TurnLimits; SMOOTH_MAX_TOKENS / SMOOTH_MAX_ITERATIONS override.

Why

A budget max_tokens can exceed what a model can physically emit → a reasoning model burns the budget on reasoning and returns EMPTY, or the upstream 400s (e.g. groq-compound caps output at 8192). And 512/6 (chat-widget sizing) starve reasoning models outright.

Files

  • dotnet/server/src/ModelInfo.cs (new), dotnet/server/src/TurnLimits.cs (new)
  • dotnet/server/src/TurnRunner.cs, FrameDispatcher.cs, host/Program.cs, aspnetcore/SmoothOperatorWebSocketExtensions.cs
  • dotnet/server/tests/MaxTokensLimitsTests.cs (new) — 9 tests: ceiling parse (happy/dropped/malformed), best-effort fetch (known/unknown/error), raised defaults, TurnRunner threads the budget.
  • dotnet/server/src/SmooAI.SmoothOperator.Server.csproj — core pin 1.3.0 → 1.5.0.

⚠️ Publish gate

The core clamp API (AgentOptions.MaxOutputTokens / ModelMaxOutputTokens) ships in smooth-operator-core#74 (NuGet 1.5.0). Until that publishes, dotnet restore on the .NET CI lane can't resolve 1.5.0 → the .NET job will be red. Verified locally against a locally-packed 1.5.0: full .slnx builds (12 projects) and all tests pass (server 65, integration 25, host 2, postgres 33, client 31/1-skip). The raised defaults are live immediately; the ceiling clamp activates once core 1.5.0 publishes and the pin resolves — same ordering the Go/Python agents used.

🤖 Generated with Claude Code

…ET server parity)

Threads a per-turn output budget + the resolved model's hard output ceiling into
the engine (which clamps max_tokens to min(budget, ceiling); EPIC th-1cc9fa), and
raises the starvation-prone defaults that make reasoning models return empty.

- ModelInfo: parse/fetch the model's max_output_tokens from the gateway's
  /model/info (C# analog of the Rust admin::model_output_ceiling + map_model_info),
  best-effort — any gateway error / unknown model / missing ceiling => null =>
  unclamped.
- TurnLimits: DI carrier with the raised defaults (max_tokens 512->8192,
  iterations 6->20) + the model ceiling; threaded through FrameDispatcher ->
  TurnRunner -> AgentOptions.
- Program.cs: resolve the ceiling once at boot (single-model server) and register
  TurnLimits; SMOOTH_MAX_TOKENS / SMOOTH_MAX_ITERATIONS override.

Bumps the core NuGet pin 1.3.0 -> 1.5.0 for the new AgentOptions clamp API. The
clamp activates once core 1.5.0 publishes; the raised defaults are live now.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5f3156e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant